module("ufe.policies.ip", package.seeall)
local m = require("ufe.ufe_module")

local name = "ip"

local function iptonumber(ipstr)
    local a, b, c, d = ipstr:byte(1, 4)
    return a * 0x1000000 + b * 0x10000 + c * 0x100 + d
end

local function check(acf)
    if not acf.ips and not acf.geo then
        return false, "ip action must have ips or geo"
    end

    local ips = {}
    if acf.ips then
        if type(acf.ips) ~= 'table' then
            return false, "wrong ips format in ip action"
        end

        for i, ip in ipairs(acf.ips) do
            local a,b,c,d,e,f,g,h = string.match(ip,"^(%d+).(%d+).(%d+).(%d+)-(%d+).(%d+).(%d+).(%d+)$")
            local start, stop
            a,b,c,d,e,f,g,h = tonumber(a),tonumber(b),tonumber(c),tonumber(d),
                                tonumber(e),tonumber(f),tonumber(g),tonumber(h)
            if (a and a >=0 and a <= 255) and (b and b >= 0 and b <= 255) and
               (c and c >=0 and c <= 255) and (d and d >= 0 and d <= 255) and
               (e and e >=0 and e <= 255) and (f and f >= 0 and f <= 255) and
               (g and g >=0 and g <= 255) and (h and h >= 0 and h <= 255)
            then
                start = a * 0x1000000 + b * 0x10000 + c * 0x100 + d
                stop = e * 0x1000000 + f * 0x10000 + g * 0x100 + h
            else
                return false, "wrong ip format in ip action"
            end

            if start > stop then
                return false, "start ip shouldn't big than end"
            end
            ips[i] = {s=start,e=stop}
        end

        -- use binary ip format
        acf.ips = ips
    end

    if acf.geo then
        if type(acf.geo) ~= 'table' then
            return false, "wrong geo format in ip action"
        end

        for _, id in ipairs(acf.geo) do
            if type(id) ~= 'string' then
                return false, 'geo block in ip action must be string'
            end
        end
    end
    return true
end

local function execute(acf)
    local ip = iptonumber(ngx.var.binary_remote_addr)
    if acf.ips then
        for _,i in ipairs(acf.ips) do
            --ngx.log(ngx.DEBUG,"ip:"..ip," start:"..i.s," end:"..i.e)
            if ip >= i.s and ip <= i.e then
                return true
            end
        end
    end

    if acf.geo then
        for _,id in ipairs(acf.geo) do
            --ngx.log(ngx.DEBUG,"id:"..id," city_id:"..ngx.var.ip_city_id)
            if ngx.var.ip_city_id == id then
                return true
            end
        end
    end

    return false
end

m.register_policy(name, {check = check, execute = execute })
